﻿using System.Threading;
using System.Threading.Tasks;

namespace ESRI.ArcGIS.Client.Tasks.Async
{
    public static partial class QueryTaskExtensions
    {
        #region Execute

        /// <summary>
        /// Executes a query against an ArcGIS Server map layer. If the query is successful, the task returns a QueryTaskResult.
        /// Otherwise, the Task.Exception is set.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="query">Specifies the attributes and spatial filter of the query.</param>
        /// <returns>a Task object with QueryTaskResult upon successful completion. Otherwise, the Task.Exception is set.</returns>
        public static Task<QueryTaskResult> ExecuteTaskAsync(this QueryTask task, Query query)
        {
            return ExecuteTaskAsync(task, query, CancellationToken.None);
        }

        /// <summary>
        /// Executes a query against an ArcGIS Server map layer. If the query is successful, the task returns a QueryTaskResult.
        /// Otherwise, the Task.Exception is set.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="query">Specifies the attributes and spatial filter of the query.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns>a Task object with QueryTaskResult upon successful completion. Otherwise, the Task.Exception is set.</returns>
        public static Task<QueryTaskResult> ExecuteTaskAsync(this QueryTask task, Query query,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<QueryTask, QueryEventArgs, QueryTaskResult>(
                t => t.ExecuteAsync(query, null),
                (t, e) => t.ExecuteCompleted += e,
                (t, e) => t.ExecuteCompleted -= e,
                e => new QueryTaskResult(e), 
                cancellationToken);
        }

        #endregion

        #region ExecuteCount

        /// <summary>
        /// Executes a query against an ArcGIS Server map layer and counts the results.
        /// If the query is successful, the task returns an integer result. Otherwise, the Task.Exception is set.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="query">Specifies the attributes and spatial filter of the query.</param>
        /// <returns>a Task object with QueryCountTaskResult upon successful completion. Otherwise, the Task.Exception is set.</returns>
        public static Task<QueryCountTaskResult> ExecuteCountTaskAsync(this QueryTask task, Query query)
        {
            return ExecuteCountTaskAsync(task, query, CancellationToken.None);
        }

        /// <summary>
        /// Executes a query against an ArcGIS Server map layer and counts the results.
        /// If the query is successful, the task returns an integer result. Otherwise, the Task.Exception is set.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="query">Specifies the attributes and spatial filter of the query.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns>a Task object with QueryCountTaskResult upon successful completion. Otherwise, the Task.Exception is set.</returns>
        public static Task<QueryCountTaskResult> ExecuteCountTaskAsync(this QueryTask task, Query query,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<QueryTask, QueryCountEventArgs, QueryCountTaskResult>(
                t => t.ExecuteCountAsync(query),
                (t,e) => t.ExecuteCountCompleted += e,
                (t,e) => t.ExecuteCountCompleted -= e,
                e => new QueryCountTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region ExecuteRelationshipQuery

        /// <summary>
        /// Executes a query to a obtain additional data from another layer/table based on a relationship
        /// defined in the service meta data.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="parameter">The parameters that define the relationship of the query.</param>
        /// <returns>a Task object with RelationshipResult upon successful completion. Otherwise, the Task.Exception is set.</returns>
        public static Task<RelationshipResult> ExecuteRelationshipQueryTaskAsync(this QueryTask task,
            RelationshipParameter parameter)
        {
            return ExecuteRelationshipQueryTaskAsync(task, parameter, CancellationToken.None);
        }

        /// <summary>
        /// Executes a query to a obtain additional data from another layer/table based on a relationship
        /// defined in the service meta data.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="parameter">The parameters that define the relationship of the query.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns>a Task object with RelationshipResult upon successful completion. Otherwise, the Task.Exception is set.</returns>
        public static Task<RelationshipResult> ExecuteRelationshipQueryTaskAsync(this QueryTask task,
            RelationshipParameter parameter, CancellationToken cancellationToken)
        {
            return task.HandleTask<QueryTask, RelationshipEventArgs, RelationshipResult>(
                t => t.ExecuteRelationshipQueryAsync(parameter),
                (t, e) => t.ExecuteRelationshipQueryCompleted += e,
                (t, e) => t.ExecuteRelationshipQueryCompleted -= e,
                e => e.Result,
                cancellationToken);
        }

        #endregion

    }
}